using Abp.Authorization;
using Abp.Collections.Extensions;
using Abp.Extensions;
using Abp.Runtime.Session;
using Abp.UI;
using Mt.Site.Application.FileFinder.Dto.Upload;
using Mt.Site.Application.FileUpload.Dto;
using Mt.Site.Application.FileUpload.Dto.MultiUpload;
using Mt.Site.Application.FileUpload.Impl;
using Mt.Site.Core.Base.BlobStoring;
using Mt.Site.Core.Base.Localization;
using Mt.Site.Core.Base.Settings;
using Mt.Site.Core.Base.Utils.FileIO;
using Mt.Site.Core.Base.Utils.FileIO.Upload;
using Mt.Site.Core.Organizations.UserCache;
using Mt.Site.Core.Storage;
using Mt.Site.Core.Storage.File_;
using Mt.Site.Core.Storage.Permissions;
using Mt.Site.Core.Synchronization;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Threading.Tasks;
using static Mt.Site.Core.Base.Utils.FileIO.Upload.UpfileTemplate;

namespace Mt.Site.Application.FileFinder
{
    [AbpAuthorize]
    public class MultipartUploadAppService : MtSiteAppServiceBase, IMultipartUploadAppService
    {
        private readonly IFileLibraryManager _fileLibraryManager;

        private readonly IFileObjectManager _fileObjectManager;

        private readonly IAppFolders _appFolders;

        private readonly IBlobContainer _container;

        private readonly IMtConfiguration _mtConfiguration;

        private readonly IUserAndUnitFinder _userAndUnitFinder;

        private readonly FileLibraryUploadHelper _fileLibraryUploadHelper;

        private readonly FileUploadHelper _fileUploadHelper;

        private MyLocalizationHelper _helper
            = new MyLocalizationHelper(MtSiteConsts.LocalizationSourceName);

        public MultipartUploadAppService(
            IAppFolders appFolders,
            IFileLibraryManager fileLibraryManager,
            IFileObjectManager fileObjectManager,
            IBlobContainer container,
            IMtConfiguration mtConfiguration,
            IUserAndUnitFinder userAndUnitFinder,
            FileLibraryUploadHelper fileLibraryUploadHelper,
            FileUploadHelper fileUploadHelper)
        {
            _appFolders = appFolders;
            _fileLibraryManager = fileLibraryManager;
            _fileObjectManager = fileObjectManager;

            _container = container;
            _mtConfiguration = mtConfiguration;

            _userAndUnitFinder = userAndUnitFinder;
            _fileLibraryUploadHelper = fileLibraryUploadHelper;
            _fileUploadHelper = fileUploadHelper;
        }

        //TODO: 文件上传前的Verify和上传后的合并都需要检查文件类型及大小

        #region 文件库切片上传

        [AbpAuthorize(FilePermissions.Pages_Library_Upload)]
        public async Task<MultipartUploadVerifyOutput> MultipartUploadVerifyAsync(MultipartUploadVerifyInput input)
        {
            if (input.ReplaceFileId > 0)
            {
                if (!await CheckReplacePermissionAsync(input.ReplaceFileId))
                {
                    throw new UserFriendlyException(L("NoDirectoryOperationPermission"));
                }
            }

            var result = new MultipartUploadVerifyOutput
            {
                UploadedList = new Dictionary<int, long>()
            };

            // 检查是否可以秒传
            var fileIsExistResult = await _fileLibraryUploadHelper.CheckFileIsExist(_fileLibraryManager, input.FileHash);
            if(fileIsExistResult != null)
            {
                var fileEntity = await _fileLibraryUploadHelper.SaveExistFileToLibraryAsync(
                    fileIsExistResult, input.ReplaceFileId, input.DirectoryId, input.FileName, _fileLibraryManager, _container);
                if (fileEntity != null)
                {
                    result.Uploaded = true;

                    List<Directories> directoriesData = await _fileLibraryManager.DirectoriesRepository.GetAllListAsync(tt => tt.Id == fileEntity.DirectoryId);
                    result.FileItem = await _fileLibraryUploadHelper.FileLibraryToFileItemAsync(directoriesData, AbpSession.GetUserId(), fileEntity, ObjectMapper, _userAndUnitFinder, _fileLibraryManager);
                    return result;
                }
            }


            CheckOldData(input.FileHash, result);

            return result;
        }

        [AbpAuthorize(FilePermissions.Pages_Library_Upload)]
        public async Task<MergeFileOutput> MergeFileAsync(MergeFileInput input)
        {
            var result = new MergeFileOutput
            {
                IsSuccess = false
            };

            var dir = Path.Combine(_appFolders.TempFileDownloadFolder, input.FileHash);
            if (Directory.Exists(dir))
            {
                var files = Directory.GetFiles(dir, "*.tdd").OrderBy(t => t).ToList();
                if (files.Count > 0 && files.Count == input.Count)
                {
                    var exName = (Path.GetExtension(input.FileName) + "").ToLower();

                    if (!_mtConfiguration.FileDownAllow_TenantSide.Contains(exName))
                    {
                        throw new UserFriendlyException(L("File_Invalid_Type_Error", _mtConfiguration.FileDownAllow_TenantSide.JoinAsString("、")));
                    }

                    var saveParams = await _fileLibraryUploadHelper.CheckFileSaveParams(input.FileName, input.ReplaceFileId, _container, _fileLibraryManager, exName);

                    await _container.MultipartUploadAsync(saveParams.SavePath, new DefaultMultipartUploadHelper(files, dir, saveParams.NewFileName), true);

                    foreach (string d in files)
                    {
                        try
                        {
                            File.Delete(d);
                        }
                        catch { }
                    }

                    try
                    {
                        Directory.Delete(dir);
                    }
                    catch { }

                    var fileLibraryItem = await _fileLibraryUploadHelper.SaveNewFileInfoToLibraryAsync(saveParams, input.FileName,
                        input.FileSize, input.FileHash, input.Dimensions, input.DirectoryId, _fileLibraryManager);

                    List<Directories> directoriesData = await _fileLibraryManager.DirectoriesRepository.GetAllListAsync(tt => tt.Id == fileLibraryItem.DirectoryId);

                    result.FileItem = await _fileLibraryUploadHelper.FileLibraryToFileItemAsync(directoriesData, AbpSession.GetUserId(), fileLibraryItem, ObjectMapper, _userAndUnitFinder, _fileLibraryManager);
                    result.IsSuccess = true;
                }
            }

            return result;
        }


        private async Task<bool> CheckReplacePermissionAsync(long id)
        {
            var fileEntity = await _fileLibraryManager.GetFileLibraryOrNullAsync(id);
            if (fileEntity == null)
            {
                return false;
            }

            var hasPermission = await IsGrantedAsync(FilePermissions.Pages_Library_Edit);
            if (!hasPermission)
            {
                hasPermission = await IsGrantedAsync(FilePermissions.Pages_Library_Edit_Self);

                if (!hasPermission)
                {
                    return false;
                }

                var dir = await _fileLibraryManager.GetDirectoriesOrNullAsync(fileEntity.DirectoryId);
                if (dir == null || !_fileLibraryManager.IsCanEdit(dir, AbpSession.GetUserId()))
                {
                    return false;
                }
            }

            return true;
        }

        #endregion

        #region 文件上传控件 切片上传校验（参照 FileUploadController）
        public async Task<ExternalUploadVerifyOutput> ExternalMultiUploadVerifyAsync(ExternalMultiUploadVerifyInput input)
        {
            await _fileUploadHelper.GetUpFileConfig(input.CustomKey, input.Alias);

            var result = new ExternalUploadVerifyOutput
            {
                UploadedList = new Dictionary<int, long>()
            };

            // 检查是否可以秒传
            var fileIsExistResult = await _fileLibraryUploadHelper.CheckFileIsExist(_fileLibraryManager, input.FileHash);
            if (fileIsExistResult != null)
            {
                var fileInfoDto = await _fileUploadHelper.CreateFileInfo(
                    _fileObjectManager, input.ContentId, input.Alias, input.IsTemporary, fileIsExistResult.UploadFileInfo, 
                    fileIsExistResult.UploadFileInfo.FileName, fileIsExistResult.ExName, fileIsExistResult.SourceName);

                await _fileUploadHelper.OnMultiUploaded(fileInfoDto.FileUrl, fileInfoDto.Id);

                await _fileUploadHelper.RemoveStaticPageAndSyncFileObject(input.ContentId, fileInfoDto.Id, OptionType.Create, AbpSession);

                result.Uploaded = true;

                result.FileItem = fileInfoDto;
                return result;
            }

            CheckOldData(input.FileHash, result);

            return result;
        }

        public async Task<ExternalUploadVerifyOutput> ExternalSingleUploadVerifyAsync(ExternalSingleUploadVerifyInput input)
        {
            Para para = await _fileUploadHelper.GetUpFileConfig(input.CustomKey, input.Alias);

            string alias = para.Alias;
            if (!input.Alias.IsNullOrWhiteSpace())
            {
                alias = input.Alias;
            }

            var result = new ExternalUploadVerifyOutput
            {
                UploadedList = new Dictionary<int, long>()
            };

            // 检查是否可以秒传
            var fileIsExistResult = await _fileLibraryUploadHelper.CheckFileIsExist(_fileLibraryManager, input.FileHash);
            if (fileIsExistResult != null)
            {
                var fileInfoDto = await _fileUploadHelper.CreateFileInfo(
                    _fileObjectManager, input.ContentId, alias, input.IsTemporary, fileIsExistResult.UploadFileInfo, 
                    fileIsExistResult.UploadFileInfo.FileName, fileIsExistResult.ExName, fileIsExistResult.SourceName);

                await _fileUploadHelper.OnSingleUploaded(fileInfoDto.FileUrl, fileInfoDto.Id);

                await _fileUploadHelper.RemoveStaticPageAndSyncFileObject(input.ContentId, fileInfoDto.Id, OptionType.Create, AbpSession);

                result.Uploaded = true;

                result.FileItem = fileInfoDto;
                return result;
            }

            CheckOldData(input.FileHash, result);

            return result;
        }

        public async Task<InternalUploadVerifyOutput> InternalSingleUploadVerifyAsync(InternalSingleUploadVerifyInput input)
        {
            await _fileUploadHelper.GetUpFileConfig(input.CustomKey, input.Alias);

            var result = new InternalUploadVerifyOutput
            {
                UploadedList = new Dictionary<int, long>()
            };

            // 检查是否可以秒传
            var fileIsExistResult = await _fileLibraryUploadHelper.CheckFileIsExist(_fileLibraryManager, input.FileHash);
            if (fileIsExistResult != null)
            {
                if (fileIsExistResult.SourceName != FileScoureName.FromLibrary)
                {
                    await _fileLibraryManager.AddToFileLibraryAutoAsync(fileIsExistResult.UploadFileInfo, fileIsExistResult.ExName);
                }

                result.Uploaded = true;

                result.FileItem = new UpdateUploadFileOut
                {
                    ImgUrl = fileIsExistResult.UploadFileInfo.FileUrl
                };
                return result;
            }

            CheckOldData(input.FileHash, result);

            return result;
        }

        public async Task<InternalUploadVerifyOutput> InternalUploadForEditVerifyAsync(InternalUploadForEditVerifyInput input)
        {
            await _fileUploadHelper.GetUpFileConfig(input.CustomKey, input.Alias);

            var result = new InternalUploadVerifyOutput
            {
                UploadedList = new Dictionary<int, long>()
            };

            // 检查是否可以秒传
            var fileIsExistResult = await _fileLibraryUploadHelper.CheckFileIsExist(_fileLibraryManager, input.FileHash);
            if (fileIsExistResult != null)
            {
                result.Uploaded = true;

                result.FileItem = new UpdateUploadFileOut
                {
                    ImgUrl = fileIsExistResult.UploadFileInfo.FileUrl
                };
                return result;
            }

            CheckOldData(input.FileHash, result);

            return result;
        }

        #endregion

        #region 文件上传控件 切片上传合并（参照 FileUploadController）

        public async Task<ExternalUploadMergeOutput> ExternalMultiUploadMergeAsync(ExternalMultiUploadMergeInput input)
        {
            var result = new ExternalUploadMergeOutput
            {
                IsSuccess = false
            };

            var dir = Path.Combine(_appFolders.TempFileDownloadFolder, input.FileHash);
            if (Directory.Exists(dir))
            {
                var files = Directory.GetFiles(dir, "*.tdd").OrderBy(t => t).ToList();
                if (files.Count > 0 && files.Count == input.Count)
                {
                    var config = await _fileUploadHelper.GetUpFileConfig(input.CustomKey, input.Alias);
                    UpfileResult upfileResult = await SaveMultiUploadFileAsync(input, config.UpFileInfo, dir, files);

                    var fileInfoDto = await _fileUploadHelper.CreateFileInfo(
                    _fileObjectManager, input.ContentId, input.Alias, input.IsTemporary, upfileResult.UploadFileInfo,
                    upfileResult.FileName, upfileResult.ExName, upfileResult.SourceName);

                    await _fileUploadHelper.OnMultiUploaded(fileInfoDto.FileUrl, fileInfoDto.Id);

                    await _fileUploadHelper.RemoveStaticPageAndSyncFileObject(input.ContentId, fileInfoDto.Id, OptionType.Create, AbpSession);

                    result.FileItem = fileInfoDto;
                }
            }

            return result;
        }

        public async Task<ExternalUploadMergeOutput> ExternalSingleUploadMergeAsync(ExternalSingleUploadMergeInput input)
        {
            var result = new ExternalUploadMergeOutput
            {
                IsSuccess = false
            };

            var dir = Path.Combine(_appFolders.TempFileDownloadFolder, input.FileHash);
            if (Directory.Exists(dir))
            {
                var files = Directory.GetFiles(dir, "*.tdd").OrderBy(t => t).ToList();
                if (files.Count > 0 && files.Count == input.Count)
                {
                    var config = await _fileUploadHelper.GetUpFileConfig(input.CustomKey, input.Alias);
                    UpfileResult upfileResult = await SaveMultiUploadFileAsync(input, config.UpFileInfo, dir, files);

                    var fileInfoDto = await _fileUploadHelper.CreateFileInfo(
                    _fileObjectManager, input.ContentId, input.Alias, input.IsTemporary, upfileResult.UploadFileInfo,
                    upfileResult.FileName, upfileResult.ExName, upfileResult.SourceName);

                    await _fileUploadHelper.OnSingleUploaded(fileInfoDto.FileUrl, fileInfoDto.Id);

                    await _fileUploadHelper.RemoveStaticPageAndSyncFileObject(input.ContentId, fileInfoDto.Id, OptionType.Create, AbpSession);

                    result.FileItem = fileInfoDto;
                }
            }

            return result;
        } 

        public async Task<InternalUploadMergeOutput> InternalSingleUploadMergeAsync(InternalSingleUploadMergeInput input)
        {
            var result = new InternalUploadMergeOutput
            {
                IsSuccess = false
            };

            var dir = Path.Combine(_appFolders.TempFileDownloadFolder, input.FileHash);
            if (Directory.Exists(dir))
            {
                var files = Directory.GetFiles(dir, "*.tdd").OrderBy(t => t).ToList();
                if (files.Count > 0 && files.Count == input.Count)
                {
                    var config = await _fileUploadHelper.GetUpFileConfig(input.CustomKey, input.Alias);
                    UpfileResult upfileResult = await SaveMultiUploadFileAsync(input, config.UpFileInfo, dir, files);

                    await _fileLibraryManager.AddToFileLibraryAutoAsync(upfileResult.UploadFileInfo, upfileResult.ExName);

                    result.FileItem = new UpdateUploadFileOut
                    {
                        ImgUrl = upfileResult.UploadFileInfo.FileUrl
                    };
                }
            }

            return result;
        }

        public async Task<InternalUploadMergeOutput> InternalUploadForEditMergeAsync(InternalUploadForEditMergeInput input)
        {
            var result = new InternalUploadMergeOutput
            {
                IsSuccess = false
            };

            var dir = Path.Combine(_appFolders.TempFileDownloadFolder, input.FileHash);
            if (Directory.Exists(dir))
            {
                var files = Directory.GetFiles(dir, "*.tdd").OrderBy(t => t).ToList();
                if (files.Count > 0 && files.Count == input.Count)
                {
                    var config = await _fileUploadHelper.GetUpFileConfig(input.CustomKey, input.Alias);
                    UpFileInfo upFileInfo = input.IsPreviewImg ? config.ThumbnailUpFileInfo : config.UpFileInfo;
                    UpfileResult upfileResult = await SaveMultiUploadFileAsync(input, upFileInfo, dir, files);

                    await _fileLibraryManager.TryAddToFileTemporaryAutoAsync(upfileResult);

                    result.FileItem = new UpdateUploadFileOut
                    {
                        ImgUrl = upfileResult.UploadFileInfo.FileUrl
                    };
                }
            }

            return result;
        }

        private async Task<UpfileResult> SaveMultiUploadFileAsync(IMergeFileInput input, UpFileInfo upFileInfo, string dir, List<string> files)
        {
            var exName = Path.GetExtension(input.FileName).ToLower();

            //UpFileInfoExtension.CheckFile(exName, input.FileSize, info, _helper);

            UpfileResult upfileResult = new UpfileResult();
            upfileResult.ExName = exName.Substring(1);
            upfileResult.FileName = input.FileName;

            string newFileName = await UpFileInfoExtension.GetFileSavePath(null, upFileInfo, input.FileName, exName, _helper, _container);
            string savePath = UpFileInfoExtension.JoinPrefixWithFileName(upFileInfo.Directory, newFileName);

            await _container.MultipartUploadAsync(savePath, new DefaultMultipartUploadHelper(files, dir, input.FileName), true);

            foreach (string d in files)
            {
                try
                {
                    File.Delete(d);
                }
                catch { }
            }

            try
            {
                Directory.Delete(dir);
            }
            catch { }

            upfileResult.UploadFileInfo = new UploadFileInfo
            {
                FileName = newFileName,
                FileUrl = savePath,
                FileSize = input.FileSize,
                Sha1 = input.FileHash
            };
            return upfileResult;
        }

        #endregion

        private void CheckOldData(string fileHash, IUploadVerifyOutput result)
        {
            // 续传
            var dir = Path.Combine(_appFolders.TempFileDownloadFolder, fileHash);
            if (Directory.Exists(dir))
            {
                foreach (string d in Directory.GetFiles(dir, "*.tdd"))
                {
                    var name = Path.GetFileNameWithoutExtension(d);
                    if (int.TryParse(name, out int nameVal))
                    {
                        var info = FileHelper.GetFileInfo(d);
                        result.UploadedList.Add(nameVal, info.Length);
                    }
                }
            }
        }
    }
}
